import sys, os, time, random, struct, threading
import MuxSvrv5 as MuxSvr
# import Reader
import FileReader
try:
    from twisted.internet import protocol,reactor
    from twisted.internet.protocol import ReconnectingClientFactory
    import twisted.web.client as client
    from twisted.internet.task import LoopingCall
except Exception, e:
    print str(e)
    print 'please install twisted module'
    sys.exit()

sendVideo = None
sendAudio = None
sendScreen = None
HEADER = chr(0)*3 + chr(1)    # 00000001 NAL prefix

class StreamPumper(MuxSvr.MuxSvrProtocol):
    v_data = []
    a_data = []
    s_data = []
    vidx = 0
    aidx = 0
    sidx = 0
    assrc = 0
    vssrc = 0
    sssrc = 0
    ai = 0
    vi = 0
    si = 0
    lastVideo = 0
    lastScreen = 0
    RTP_HEAD_FORMAT = "!BBHII"
    NEW_DATA_HEAD="!HBI"
    NEW_SIGN_HEAD="!HB"

    def connectionMade(self):
        self.videoReader = FileReader.FileReader(self.factory.video, self.factory.screen)
        self.screenReader = FileReader.FileReader(self.factory.video, self.factory.screen)
        reactor.callFromThread(self.videoReader.readVideoData, 0)
        if FileReader.vidx == -1:
            self.lastVideo = 1
        reactor.callFromThread(self.screenReader.readScreenData, 0)
        if FileReader.sidx == -1:
            self.lastScreen = 1

        if self.factory.window != None:
            self.factory.window.Message("Connected to mux server\n")
        print "Connected to mux server"
        if self.factory.channel == "":
            url = str('http://'+ self.factory.server+
                      '/lecturer/classroom_login?classroom=%s&passwd=%s'\
                      %(self.factory.classroom, self.factory.password))
            time.sleep(1)        # to avoid unknown error
            deferred = client.getPage(url)
            deferred.addCallback(self.extractKey)
        else:
            print self.factory.channel
            self.channel = self.factory.channel
            self.channel_key = self.factory.key
            self.try2Login(1)

    def extractKey(self, content):
        start_index = content.find("<uid>")
        end_index = content.find("</uid>")
        self.channel = content[start_index+5:end_index]
        if self.factory.window != None:
            self.factory.window.Message("channel ID is %s\n"%self.channel)
        print "channel ID is %s"%self.channel
        start_index = content.find("<key>")
        end_index = content.find("</key>")
        self.channel_key = content[start_index+5:end_index]
        if self.factory.window != None:
            self.factory.window.Message("extract the channel key successfully\n")
        print "extract the channel key successfully"
        self.try2Login(1)

    def try2Login(self, force_drop):
        if not force_drop:
            msg = str("login_send;%s"%self.channel)
            msg_len = len(msg)+1
            timestamp = int(time.time()*1000)&0xFFFFFFFF
            packet = struct.pack(self.NEW_SIGN_HEAD, msg_len, 0)
            self.translate.write(packet)
            self.translate.write(msg)
            #self.sendSignalingMsg(msg)
        else:
            msg = str("login_send;%s;drop_existing_encoders;"%self.channel)
            msg_len = len(msg)+1
            timestamp = int(time.time()*1000)&0xFFFFFFFF
            packet = struct.pack(self.NEW_SIGN_HEAD, msg_len, 0)
            self.transport.write(packet)
            self.transport.write(msg)
            #self.sendSignalingMsg(msg)
        self.handleSignalingMsg = self._handleSvrChallengeMsg

    def _handleSvrChallengeMsg(self, payload):
        if self.factory.window != None:
            self.factory.window.Message("handling challenge message from server\n")
        print "handling challenge message from server"
        args = payload.split(";")
        if len(args)==2:        # different server differs...
            cmd, svr_nonce = args
            if cmd=='challenge':
                client_nonce = MuxSvr.generateNonceStr()
                digest = MuxSvr.computeDigest(self.channel_key, svr_nonce, client_nonce)
                self.handleSignalingMsg = self._handleAuthenticationResult
                msg = str("challenge_response;%s;%s"%(client_nonce,digest))
                msg_len = len(msg)+1
                timestamp = int(time.time()*1000)&0xFFFFFFFF
                packet = struct.pack(self.NEW_SIGN_HEAD, msg_len, 0)
                self.transport.write(packet)
                self.transport.write(msg)
                #self.sendSignalingMsg(msg)
                return
        if len(args)==3:
            cmd, svr_nonce,blank = args
            if cmd=='challenge':
                client_nonce = MuxSvr.generateNonceStr()
                digest = MuxSvr.computeDigest(self.channel_key, svr_nonce, client_nonce)
                self.handleSignalingMsg = self._handleAuthenticationResult
                msg = str("challenge_response;%s;%s"%(client_nonce,digest))
                msg_len = len(msg)+1
                timestamp = int(time.time()*1000)&0xFFFFFFFF
                packet = struct.pack(self.NEW_SIGN_HEAD, msg_len, 0)
                self.transport.write(packet)
                self.transport.write(msg)
                #self.sendSignalingMsg(msg)
                return
        #get an unexpected msg from the svr, just abort
        self.transport.loseConnection()

    def _handleAuthenticationResult(self, payload):
        args = payload.split(";")
        cmd = args[0]
        if cmd=='login_denied':
            if self.factory.window != None:
                self.factory.window.Message("login denied by server\n")
            print "login denied by server"
            if len(args)>1:
                if self.factory.window != None:
                    self.factory.window.Message("svr said: %s\n"%args[1])
                print "svr said: %s"%args[1]
            self.transport.loseConnection()
            return
        elif cmd=='login_accepted':
            if self.factory.window != None:
                self.factory.window.Message("login accepted by svr, begin to send data.\n")
            print "login accepted by svr, begin to send data"
            self.v3MsgReceived = self._realMsgReceived
            while (self.assrc == self.vssrc or self.assrc == self.sssrc or self.vssrc == self.sssrc):
                self.assrc = random.randint(0, 65535)
                self.vssrc = random.randint(0, 65535)
                self.sssrc = random.randint(0, 65535)
 #          LoopingCall(self._sendReport).start(interval=30, now=True)

            global sendVideo, sendAudio, sendScreen
            self.v_data = FileReader.v_data[:]
            self.s_data = FileReader.s_data[:]
            self.a_data = FileReader.a_data[:]
            reactor.callFromThread(self.videoReader.readVideoData, FileReader.vidx)
            reactor.callFromThread(self.screenReader.readScreenData, FileReader.sidx)
            sendVideo = LoopingCall(self._sendVideoRTP)
            sendAudio = LoopingCall(self._sendAudioRTP)
            sendScreen = LoopingCall(self._sendScreenRTP)
            sendVideo.start(interval=0.1, now=True)
            sendAudio.start(interval=0.02, now=True)
            sendScreen.start(interval=0.1, now=True)
            return
        #get an unexpected msg from the svr, just abort
        self.transport.loseConnection()


    def _realMsgReceived(self, type, timestamp, payload):
        print payload

#===============================================================================
#    def _sendVideoData(self):        # send V3message
#        if self.vidx < len(self.v_data):
#            self.sendV3Msg(MuxSvr.VIDEO_FRAME_LAST_PACKET, self.v_data[self.vidx])
#            self.vidx += 1
#        else:
#            lastVideoData = 1
#            if (lastAudioData and lastScreenData and lastVideoData):
#                self._end()
#===============================================================================

    def _sendVideoRTP(self):        # send RTP packet
        if self.vi < len(self.v_data):
            payload_all = self.v_data[self.vi]
            if len(payload_all)>0:
                timestamp = int(time.time()*1000)&0xFFFFFFFF
                lastIndex = 0
                index = payload_all.find(HEADER)
                while (index != -1):
                    if lastIndex == 0:
                        payload = payload_all[lastIndex:index]
                    else:
                        payload = payload_all[lastIndex+4:index]
#===============================================================================
#                    #RTP packet, format changed on Nov.1
#                    packet = struct.pack(self.RTP_HEAD_FORMAT, 128, 97, self.vidx, timestamp, self.vssrc)
#                    payload_len = len(packet+payload)
#                    head = struct.pack(self.RTP_INTERLEAVING_MSG_HEAD_FORMAT, 0x24, 12, payload_len)#0x24='$'
#                    self.transport.write(head)
#                    self.transport.write(packet)
#                    self.transport.write(payload)
#===============================================================================

                    payload_len = len(payload)+5
                    packet = struct.pack(self.NEW_DATA_HEAD, payload_len, 12, timestamp)
                    #print repr(packet)
                    self.transport.write(packet)
                    self.transport.write(payload)
                    lastIndex = index
                    index = payload_all.find(HEADER, lastIndex+4)
                if lastIndex == 0:
                    payload = payload_all[lastIndex:len(payload_all)]
                else:
                    payload = payload_all[lastIndex+4:len(payload_all)]
#===============================================================================
#                packet = struct.pack(self.RTP_HEAD_FORMAT, 128, 225, self.vidx, timestamp, self.vssrc)
#                payload_len = len(packet+payload)
#                head = struct.pack(self.RTP_INTERLEAVING_MSG_HEAD_FORMAT, 0x24, 12, payload_len)#0x24='$'
#                self.transport.write(head)
#                self.transport.write(packet)
#                self.transport.write(payload)
#===============================================================================

                payload_len = len(payload)+5
                packet = struct.pack(self.NEW_DATA_HEAD, payload_len, 12, timestamp)
                self.transport.write(packet)
                self.transport.write(payload)
                lastIndex = 0
            self.vidx += 1
            self.vi += 1
        else:
            if self.lastVideo == 1:
                self._end()
                return
            if FileReader.vidx == 0:
                self.lastVideo = 1
            self.v_data = FileReader.v_data[:]
            self.vi = 0
            if self.screenReader.type == 1:
                self.a_data = FileReader.a_data[:]
                self.ai = 0
            reactor.callFromThread(self.videoReader.readVideoData, FileReader.vidx)


#===============================================================================
#    def _sendAudioData(self):
#        if self.ai < len(self.a_data):
#            self.sendV3Msg(MuxSvr.AUDIO_DATA_TYPE_SPEEX, self.a_data[self.ai])
#            self.aidx += 1
#            self.ai += 1
#===============================================================================

    def _sendAudioRTP(self):
        if self.ai < len(self.a_data):
            payload = self.a_data[self.ai]
            if len(payload)>0:
                seq = self.aidx
                timestamp = int(time.time()*1000)&0xFFFFFFFF
#===============================================================================
#                packet = struct.pack(self.RTP_HEAD_FORMAT, 128, 96, seq, timestamp, self.assrc)
#                payload_len = len(packet+payload)
#                head = struct.pack(self.RTP_INTERLEAVING_MSG_HEAD_FORMAT, 0x24, 10, payload_len)#0x24='$'
#                self.transport.write(head)
#                self.transport.write(packet+payload)
#===============================================================================
                payload_len = len(payload)+5
                packet = struct.pack(self.NEW_DATA_HEAD, payload_len, 10, timestamp)
                self.transport.write(packet)
                self.transport.write(payload)
            self.aidx += 1
            self.ai += 1

    def _sendScreenRTP(self):
        if self.si < len(self.s_data):
            buffer = self.s_data[self.si]
            last = len(buffer)
            index = 0
            size = 0

            while index < len(buffer):
                size = (16 ** 2 * ord(buffer[index + 1]) + ord(buffer[index]))
                timestamp = int(time.time()*1000)&0xFFFFFFFF
                seq = self.sidx
                if index+size+2 < len(buffer):
                    #packet = struct.pack(self.RTP_HEAD_FORMAT, 128, 98, seq, timestamp, self.sssrc)
                    payload = buffer[index+2:index+size+2]
#===============================================================================
#                    payload_len = len(packet+payload)
#                    head = struct.pack(self.RTP_INTERLEAVING_MSG_HEAD_FORMAT, 0x24, 14, payload_len)#0x24='$'
#                    self.transport.write(head)
#                    self.transport.write(packet+payload)
#===============================================================================

                    payload_len = len(payload)+5
                    packet = struct.pack(self.NEW_DATA_HEAD, payload_len, 14, timestamp)
                    self.transport.write(packet)
                    self.transport.write(payload)

                else:
                    #packet = struct.pack(self.RTP_HEAD_FORMAT, 128, 226, seq, timestamp, self.sssrc)
                    payload = buffer[index+2:index+size+2]
#===============================================================================
#                    payload_len = len(packet+payload)
#                    head = struct.pack(self.RTP_INTERLEAVING_MSG_HEAD_FORMAT, 0x24, 14, payload_len)#0x24='$'
#                    self.transport.write(head)
#                    self.transport.write(packet+payload)
#===============================================================================
                    payload_len = len(payload)+5
                    packet = struct.pack(self.NEW_DATA_HEAD, payload_len, 14, timestamp)
                    self.transport.write(packet)
                    self.transport.write(payload)
                index += size+2
            self.sidx += 1
            self.si += 1
        else:
            if self.lastScreen == 1:
                self._end()
                return
            if FileReader.sidx == 0:
                self.lastScreen = 1
            self.s_data = FileReader.s_data[:]
            self.si = 0
            if self.screenReader.type == 2:
                self.a_data = FileReader.a_data[:]
                self.ai = 0
            reactor.callFromThread(self.screenReader.readScreenData, FileReader.sidx)

#===============================================================================
#    def _sendScreenData(self):
#        if self.si < len(self.s_data):
#            buffer = self.s_data[self.si]
#            last = len(buffer)
#            index = 0
#            size = 0
#            while index < len(buffer):
#                size = (16 ** 2 * ord(buffer[index + 1]) + ord(buffer[index]))
#                if index+size+4 < len(buffer):
#                    self.sendV3Msg(MuxSvr.SCREEN_FRAME_PACKET, buffer[index+4:index+size+4])
#                else:
#                    self.sendV3Msg(MuxSvr.SCREEN_FRAME_LAST_PACKET, buffer[index+4:index+size+4])
#                index += size+4
#
#            self.sidx += 1
#            self.si += 1
#        else:
#            self._end()
#===============================================================================


#===============================================================================
#    def _sendReport(self):
#        msg = "report:cpu_usage=31;buffer_level=31234;screen_rate=18323;video_rate=72344;audio_rate=16123"
#        self.sendSignalingMsg(msg)
#===============================================================================

    def _end(self):
        if self.factory.loopMode:
            self.vidx = 0
            self.aidx = 0
            self.sidx = 0
            self.vi = 0
            self.ai = 0
            self.si = 0
            self.v_data = FileReader.v_data[:]
            self.a_data = FileReader.a_data[:]
            self.s_data = FileReader.s_data[:]
            lastVideo = 0
            lastScreen = 0
            reactor.callFromThread(self.videoReader.readVideoData, FileReader.vidx)
            reactor.callFromThread(self.screenReader.readScreenData, FileReader.sidx)

        else:
            msg = "logout"
            self.sendSignalingMsg(msg)
            time.sleep(1)
            self.transport.loseConnection()

#class MyEngine(protocol.ClientFactory):    # use ReconnectingClientFactory instead
#    protocol = StreamPumper

def connect(mux_svr_ip, server, mux_svr_port, classroom, password,
       channel, key, video, screen, loopMode, maxRetries, interval, window):
    my_engine = streamerFactory()
    my_engine.server = server
    my_engine.classroom = classroom
    my_engine.password = password
    my_engine.channel = channel
    my_engine.key = key
    my_engine.video = video
    my_engine.screen = screen
    my_engine.loopMode = loopMode
    my_engine.window = window
    my_engine.initialDelay = interval
    my_engine.delay = interval
    my_engine.maxRetries = maxRetries
    reactor.connectTCP( mux_svr_ip, mux_svr_port, my_engine)

class streamerFactory(ReconnectingClientFactory):
    protocol = StreamPumper
    factor = 1
    jitter = 0
    def buildProtocol(self, addr):
        self.resetDelay()
        p = self.protocol()
        p.factory = self
        return p

    def clientConnectionLost(self, connector, reason):
        if self.window != None:
            self.window.Message(str(reason)+"\n")
        print "Lost connection. Reason:", reason
        if sendVideo.running:    # stop loopingcall
            sendVideo.stop()
            sendAudio.stop()
            sendScreen.stop()
        ReconnectingClientFactory.clientConnectionLost(self, connector, reason)

    def clientConnectionFailed(self, connector, reason):
        if self.window != None:
            self.window.Message(str(reason)+"\n")
        print 'Connection failed. Reason:', reason
        if self.retries < self.maxRetries:
            if self.window != None:
                self.window.Message("streamer will reconnect %s seconds later\n"%self.delay)
            print "streamer will reconnect %s seconds later"%self.delay
        else:
            if self.window != None:
                self.window.Message("After reconnecting %s times the streamer still could not connect to the server."%self.maxRetries)
            print "After reconnecting %s times the streamer still could not connect to the server."%self.maxRetries
        ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)


class Uploader(threading.Thread):
    def __init__(self, server, port, classroom, password, channel, key,
                 video, screen, loopMode, maxRetries, interval, window):
        threading.Thread.__init__(self)
        self.server = server
        self.port = port
        self.classroom = classroom
        self.password = password
        self.channel = channel
        self.key = key
        self.video = video
        self.screen = screen
        self.maxRetries = maxRetries
        self.interval = interval
        self.loopMode = loopMode
        self.window = window
        if self.window != None:
            self.window.Message("mux server is %s:%d\n"%(server, port))
        print "mux server is %s:%d"%(server, port)


    def run(self):
        reactor.resolve(self.server).addCallback(connect, self.server, self.port, self.classroom, self.password,
         self.channel, self.key,
         self.video, self.screen, self.loopMode,
         self.maxRetries, self.interval, self.window)
        reactor.run(installSignalHandlers = 1)

    def stop(self):
        reactor.stop()